'''
The module contains the protocol that the a peer use to communicate
with the tracker.

@author: Hatem Oraby
'''


from twisted.protocols.basic import LineOnlyReceiver
from twisted.internet import reactor
from twisted.internet.protocol import ClientCreator, Protocol
from twisted.internet.defer import Deferred

from util import Peer, TrackerCmds, TrackerResponse



class Register(LineOnlyReceiver):
    
    class state:
        NothingYet = 0
        Register = 1
        RecievingPeers = 2
        Done = 3
        
    def __init__(self, port, defer):
        self._state = self.state.NothingYet
        self._port = port
        self._defer = defer
        self._peers = []
    
    def lineReceived(self,line):
        if self.state == self.state.NothingYet:
            raise "E10: Tracker is fooling around."
        try:
            command, params = line.split(":",1)
        except ValueError:
            #Wrong command sent
            raise  "E11: Tracker sent wrong format:" + line
 
        if command == TrackerResponse.registerResponse:
            if self._state == self.state.Register:
                self._state = self.state.RecievingPeers
            else:
                raise "E12: Unexpected response"
        elif command == TrackerResponse.sendeingPeers:
            if self._state != self.state.RecievingPeers:
                raise "E13: Unexpected response"
            self._addPeer(params)
        elif command == TrackerResponse.doneSending:
            if self._state != self.state.RecievingPeers:
                raise "E14: Tracker finished before starting to send"
            self._state = self.state.Done
            self._defer.callback(self._peers)
            self.transport.loseConnection()
        else:
            raise "E15:Unknown tracker response received."
        
    def connectionMade(self):
        addr = self.transport.getHost()
        IP = addr.host
        port = str(self._port)
        addr = IP + ":" + port
        command = TrackerCmds.register + ":" + addr
        self.sendLine(command)
        self._state = self.state.Register
        
    def _addPeer(self,params):
        try:
            #Parse params, but maybe peer address is an URL or machine name
            # (not IP) so if we split on the first ":" then we would get 
            #parts of the url so we split on last ":" from end of the string. 
            index = params.rindex(":")#Raises ValueError if : not found
            IP = params[:index]
            port = params[index+1:]#Raises Index error if nothing exists after :
        except (ValueError,IndexError):
            msg = "E14: Invalid format"
            self._defer.errback(msg)
            raise msg
        try:
            port = int(port)
        except ValueError:
            msg = "E15: Invalid port provided."
            self._defer.errback(msg)
            raise  msg
        peer = Peer(IP, port)
        self._peers.append(peer)
        

def registerWithTracker(trackerAddr, portToRegister, defer):
    try:
        trackerIP, trackerPort = trackerAddr
        trackerPort = int(trackerPort)
    except (TypeError,ValueError):
        raise "trackerAaddr argument cannot be divided to IP and int(port)"
    f = ClientCreator(reactor, Register, portToRegister, defer)
    d = f.connectTCP(trackerIP, trackerPort)
    return d

def main():
    def success(something):
        print "From success"
        print str(something)
        
    def failed(reason):
        print "Connection Failed"
        reactor.stop()
    dRegister = Deferred()
    dRegister.addCallbacks(success, failed)
    registerWithTracker(("127.0.0.1", 2000), 3000, dRegister)
    reactor.run()

if __name__ == '__main__':
    main()